import 'dart:math';

import 'package:flutter/material.dart';

import '../flutter_lui.dart';

/// 悬浮导航扩展开的方向
enum LFlowNavigationExpandDirection {
  left,
  right,
  top,
  bottom,
  topLeft,
  topRight,
  bottomLeft,
  bottomRight,
}

/// 悬浮导航栏
class LFlowNavigationBar extends StatefulWidget {
  /// 展开半径
  final double expandRadius;

  /// 每个item的宽度
  final double itemWidth;

  /// 每个item的背景颜色
  final Color backgroundColor;

  /// 主item
  final Widget mainItem;

  /// 菜单项
  final List<Widget> items;

  /// 展开方向
  final LFlowNavigationExpandDirection direction;

  /// 点击item的回调
  final void Function(int index)? onChange;
  const LFlowNavigationBar(
      {required this.itemWidth,
      required this.expandRadius,
      required this.backgroundColor,
      required this.mainItem,
      required this.items,
      required this.direction,
      this.onChange,
      Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => _FlowNavigationBar();
}

class _FlowNavigationBar extends State<LFlowNavigationBar>
    with SingleTickerProviderStateMixin {
  OverlayEntry? _overlayEntry;

  final GlobalKey _mainItemKey = GlobalKey();

  handleItemTap(int index) {
    if (index == -1) {
      handleCloseOverlayEntery();
    } else if (widget.onChange != null) {
      widget.onChange!(index);
    }
  }

  handleCloseOverlayEntery() {
    if (_overlayEntry != null) {
      _overlayEntry!.remove();
      _overlayEntry = null;
    }
  }

  handleExpand() {
    RenderBox? box =
        _mainItemKey.currentContext?.findRenderObject() as RenderBox?;
    if (box == null) return;
    var position = box.localToGlobal(Offset.zero);
    double mainLeft = position.dx;
    double mainTop = position.dy;
    OverlayState? state = Overlay.of(context);
    if (state == null) return;
    _overlayEntry = OverlayEntry(
      builder: (context) => _FlowNavigationBarEntery(
          itemWidth: widget.itemWidth,
          expandRadius: widget.expandRadius,
          backgroundColor: widget.backgroundColor,
          items: widget.items,
          direction: widget.direction,
          onChange: handleItemTap,
          mainItem: widget.mainItem,
          mainLeft: mainLeft,
          mainTop: mainTop),
    );
    state.insert(_overlayEntry!);
  }

  @override
  Widget build(BuildContext context) {
    return LBox(
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.3),
          blurRadius: 10,
          spreadRadius: 0,
        ),
      ],
      key: _mainItemKey,
      onTap: handleExpand,
      width: widget.itemWidth,
      height: widget.itemWidth,
      alignment: Alignment.center,
      backgroundColor: widget.backgroundColor,
      borderRadius: [widget.itemWidth / 2],
      child: widget.mainItem,
    );
  }
}

class _ItemPosition {
  double left;
  double top;
  _ItemPosition({required this.left, required this.top});
}

class _FlowNavigationBarEntery extends StatefulWidget {
  final double expandRadius;
  final double itemWidth;
  final Color backgroundColor;
  final List<Widget> items;
  final Widget mainItem;
  final double mainLeft;
  final double mainTop;
  final LFlowNavigationExpandDirection direction;
  final void Function(int index) onChange;
  const _FlowNavigationBarEntery(
      {required this.itemWidth,
      required this.expandRadius,
      required this.backgroundColor,
      required this.items,
      required this.direction,
      required this.onChange,
      required this.mainItem,
      required this.mainLeft,
      required this.mainTop,
      Key? key})
      : super(key: key);
  @override
  State<StatefulWidget> createState() => __FlowNavigationBarEntery();
}

class __FlowNavigationBarEntery extends State<_FlowNavigationBarEntery>
    with SingleTickerProviderStateMixin {
  final List<_ItemPosition> _itemPositions = [];
  late AnimationController _animationController;

  double running = 0;
  @override
  void initState() {
    super.initState();
    calcPosition();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    var runningTween = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
        parent: _animationController, curve: Curves.easeOutBack));
    runningTween.addListener(() {
      setState(() {
        running = runningTween.value;
      });
    });
    _animationController.forward();
  }

  void calcPosition() {
    _itemPositions.clear();
    switch (widget.direction) {
      case LFlowNavigationExpandDirection.top:
        final total = widget.items.length + 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            left: cos(pi * (total - i - 1) / total) * widget.expandRadius,
            top: -sin(pi * (total - i - 1) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.bottom:
        for (var i = 0; i < widget.items.length; i++) {
          final total = widget.items.length + 1;
          _itemPositions.add(_ItemPosition(
            left: cos(pi * (total - 1 - i) / total) * widget.expandRadius,
            top: sin(pi * (total - 1 - i) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.left:
        final total = widget.items.length + 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: cos(pi * (total - 1 - i) / total) * widget.expandRadius,
            left: -sin(pi * (total - 1 - i) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.right:
        final total = widget.items.length + 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: cos(pi * (total - 1 - i) / total) * widget.expandRadius,
            left: sin(pi * (total - 1 - i) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.topLeft:
        final total = widget.items.length - 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: -cos(pi / 2 * i / total) * widget.expandRadius,
            left: -sin(pi / 2 * i / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.topRight:
        final total = widget.items.length - 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: -cos(pi / 2 * (i) / total) * widget.expandRadius,
            left: sin(pi / 2 * (i) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.bottomLeft:
        final total = widget.items.length - 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: cos(pi / 2 * (i) / total) * widget.expandRadius,
            left: -sin(pi / 2 * (i) / total) * widget.expandRadius,
          ));
        }
        break;
      case LFlowNavigationExpandDirection.bottomRight:
        final total = widget.items.length - 1;
        for (var i = 0; i < widget.items.length; i++) {
          _itemPositions.add(_ItemPosition(
            top: cos(pi / 2 * (i) / total) * widget.expandRadius,
            left: sin(pi / 2 * (i) / total) * widget.expandRadius,
          ));
        }
        break;
    }
  }

  handleExpand() async {
    await _animationController.reverse();
    widget.onChange(-1);
  }

  @override
  void dispose() {
    super.dispose();
    _animationController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return LBox(
        width: PageSize.width,
        height: PageSize.height,
        backgroundColor: Colors.transparent,
        onTap: handleExpand,
        child: Stack(
          children: [
            ...List.generate(widget.items.length, (index) {
              _ItemPosition position = _itemPositions[index];
              return Positioned(
                  left: position.left * running + widget.mainLeft,
                  top: position.top * running + widget.mainTop,
                  child: RotationTransition(
                    turns: _animationController,
                    child: LBox(
                      boxShadow: [
                        BoxShadow(
                          color: Colors.black.withOpacity(0.3),
                          blurRadius: 10,
                          spreadRadius: 0,
                        ),
                      ],
                      onTap: () => widget.onChange(index),
                      width: widget.itemWidth,
                      height: widget.itemWidth,
                      alignment: Alignment.center,
                      backgroundColor: widget.backgroundColor,
                      borderRadius: [widget.itemWidth / 2],
                      child: widget.items[index],
                    ),
                  ));
            }),
            Positioned(
              left: widget.mainLeft,
              top: widget.mainTop,
              child: RotationTransition(
                turns: _animationController,
                child: LBox(
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.3),
                      blurRadius: 10,
                      spreadRadius: 0,
                    ),
                  ],
                  onTap: handleExpand,
                  width: widget.itemWidth,
                  height: widget.itemWidth,
                  alignment: Alignment.center,
                  backgroundColor: widget.backgroundColor,
                  borderRadius: [widget.itemWidth / 2],
                  child: widget.mainItem,
                ),
              ),
            ),
          ],
        ));
  }
}
